#include "RpcProvider.hpp"
#include "RpcReadConfFile.hpp"
#include "transmissionprotocol.pb.h"
#include <iostream>
#include <functional>

std::string ProjectName = "RPC Network Communication Framework";

void RpcProvider::OnConnection(const muduo::net::TcpConnectionPtr& conn)
{
     if(!conn->connected())
     {
        //放入log日志中
        conn->shutdown();
     }
     return;
}


//只有咱们注册的回调函数OnMessage才能很好的知道有人发起了业务请求

//因为的字节流传输，所以我们得解决粘包问题，在读取的时候必须约定好协议 
//在json中我们约定了键值对作为传输协议，那么在protobuf中没有键值对，在传输的时候直接
//(NameStr_size)ServerName methodName (ParameterStr_size)parameter
void RpcProvider::OnMessage(const muduo::net::TcpConnectionPtr& conn,
                            muduo::net::Buffer* buffer,
                            muduo::Timestamp timeStamp)
{
    std::string recv_buf = buffer->retrieveAllAsString();
     
    //直接截取前四个字节，copy的好处就在于他的参数是一个输出型参数
    uint32_t headerSize = recv_buf.copy((char*)&headerSize,4,0);
    //注意了本headerSize是包含了后面的参数的长度的
    std::string Names = recv_buf.substr(4,headerSize);

    TransmissionProtocol::Transmission transmission;
    if(!transmission.ParseFromString(Names))
    {
        std::cout << "transmission ParseFromString fail" << std::endl;
        exit(EXIT_FAILURE);
    }

    std::string serverName = transmission.servername();
    std::string methodName = transmission.methodname();
    uint32_t argsSize = transmission.args_size();

    std::string args = recv_buf.substr(4+headerSize,argsSize);

    std::cout << "serverName :"<< " " <<serverName << ""<< std::endl; 
    std::cout << "methodName :"<< " " <<methodName << ""<< std::endl; 
    std::cout << "args       :"<< " " <<args << ""<< std::endl; 

    //在_Map中查找是否有对应的服务和对应的服务方法
    auto itService = _serviceInfoMap.find(serverName);
    if(itService == _serviceInfoMap.end())
    {
        std::cout << serverName << " " << "is no exist" << std::endl;
        exit(EXIT_FAILURE);
    }

    ServiceInfo SI = itService->second;
    //拿到*service
    ::google::protobuf::Service* service = SI.pService;

    auto itMethod =  SI._serviceMethodMap.find(methodName);
    if(itMethod  == SI._serviceMethodMap.end())
    {
        std::cout << methodName  << " " << "is no exist" << std::endl;
        exit(EXIT_FAILURE);
    }

    //拿到methodDesc
    const::google::protobuf::MethodDescriptor* methodDesc = itMethod->second;

}

bool RpcProvider::PrintRegisterService()
{
     bool ret = false;
     for(auto& service : _serviceInfoMap)
     {
         ServiceInfo SI = service.second;
         std::cout << "Service:" <<  service.first << " " << "Include Method:";
         for(auto& serviceMethod : SI._serviceMethodMap)
         {
            std::cout << serviceMethod.second->name() << " ";
         }
         std::cout << std::endl;
         if(ret == false)
         ret = true;
     }

     return ret;
}

//如何实现NotifyService？, 首先我们不能确定用户什么时候调用执行已经注册了的不同模块的业务，不知道何时处理这个业务
//所以这个业务交给回调函数，注册在OnMessage中，当有事件请求，就会调用OnMessage
//所以NotiFfyService应该用map保留调用关系，方便OnMesssage可以调用到对应的业务
bool RpcProvider::NotifyService(::google::protobuf::Service* serivce)
{
     //本api的作用就是填充“业务注册表”

     ServiceInfo serviceInfo;
     serviceInfo.pService = serivce;

     static const ::google::protobuf::ServiceDescriptor* pServiceDesc = serivce->GetDescriptor();
     int methodCount = pServiceDesc->method_count();
     const std::string serviceName  =  pServiceDesc->name();


     for(int i = 0; i < methodCount ; ++i)
     {
        const  ::google::protobuf::MethodDescriptor* pMethodDesc = pServiceDesc->method(i);
        const std::string methodName = pMethodDesc->name();

        serviceInfo._serviceMethodMap.insert(std::make_pair(methodName,pMethodDesc));
     }
     _serviceInfoMap.insert(std::make_pair(serviceName,serviceInfo));

     return PrintRegisterService();     
}

//即启动muduo网络库,初始化moduo网络库,写好对应的回调函数
void RpcProvider::start()
{
     //填充TcpServer以启动服务

     std::string serverIp = RpcReadConfFile::GetRpcReadConfFile().GetConfMapMessage("serverip"); 
     std::string stringServerPort = RpcReadConfFile::GetRpcReadConfFile().GetConfMapMessage("serverport");

     std::cout << "IP:" << serverIp << " " << "port:" << stringServerPort << std::endl;

     if(serverIp == "" || stringServerPort == "")
     std::cout << "error: not found ConfMessage of Server" << std::endl;



     uint16_t port = atoi(stringServerPort.c_str());

     muduo::net::InetAddress listenAddr(serverIp,port);
     muduo::net::TcpServer tcpServer(&_eventloop,listenAddr,ProjectName);

     //注册回调函数，设置线程数

     //由于这里我们要设置一个类内的回调函数，类内的函数调用是需要对象的，只传入函数名是不行的，我们之间使用bind绑定器
     tcpServer.setConnectionCallback(std::bind(&RpcProvider::OnConnection,this,std::placeholders::_1));
     tcpServer.setMessageCallback(std::bind(&RpcProvider::OnMessage,this,
     std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));

     tcpServer.setThreadNum(4);

     // 开始监听并启动loop循环
     tcpServer.start();
     _eventloop.loop();

     //注意 ：以上代码更不就不需要什么什么异常判断路径，因为muduo网络库里很好的实现的对应的log日志
}